package com.springboot.file.util.easyexcel;

import com.springboot.file.util.easyexcel.exception.ExcelProcessingException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;

/**
 * 异常处理工具类
 * 提取Excel处理相关异常处理的通用逻辑
 */
@Slf4j
public class ExceptionUtils {
    
    // 定义常量，避免重复字符串
    private static final String CLASS_EXCEL_VALIDATION_EXCEPTION = "ExcelValidationException";
    private static final String CLASS_BUSINESS_EXCEPTION = "BusinessException";
    private static final String CLASS_TRANSACTION_PROCESSING_EXCEPTION = "TransactionProcessingException";
    
    // 定义常量，避免重复方法名称
    private static final String METHOD_GET_ORIGINAL_CAUSE = "getOriginalCause";
    private static final String METHOD_GET_ROW_INDEX = "getRowIndex";
    private static final String METHOD_GET_SHEET_NAME = "getSheetName";
    private static final String METHOD_GET_SHEET_INDEX = "getSheetIndex";
    private static final String METHOD_GET_ERROR_MESSAGES = "getErrorMessages";
    
    /**
     * 异常结果类型，用于统一处理各种异常
     */
    public enum ExceptionType {
        BUSINESS,
        VALIDATION,
        OTHER
    }

    /**
     * 异常处理结果
     */
    public static class ExceptionHandlingResult {
        @Getter
        private final ExceptionType exceptionType;
        @Getter
        private final Throwable exception;
        @Getter
        private final String errorMessage;
        private final boolean shouldThrow;
        
        public ExceptionHandlingResult(ExceptionType exceptionType, Throwable exception, 
                                      String errorMessage, boolean shouldThrow) {
            this.exceptionType = exceptionType;
            this.exception = exception;
            this.errorMessage = errorMessage;
            this.shouldThrow = shouldThrow;
        }

        public boolean shouldThrow() {
            return shouldThrow;
        }
        
        public RuntimeException getAsRuntimeException() {
            return (RuntimeException) exception;
        }
    }

    /**
     * 私有构造函数，防止实例化
     */
    private ExceptionUtils() {
        throw new IllegalStateException("工具类不应被实例化");
    }

    /**
     * 查找并提取异常链中的Excel校验异常
     * 
     * @param exception 原始异常
     * @return 提取出的ExcelValidationException，如果不存在则返回null
     */
    public static Object extractValidationException(Throwable exception) {
        if (exception == null) {
            return null;
        }
        
        // 如果异常本身就是ExcelValidationException，直接返回
        if (isExceptionOfType(exception, CLASS_EXCEL_VALIDATION_EXCEPTION)) {
            return exception;
        }
        
        // 如果是TransactionProcessingException，尝试从其原始原因中提取
        if (isExceptionOfType(exception, CLASS_TRANSACTION_PROCESSING_EXCEPTION)) {
            Throwable originalCause = getOriginalCauseFromTransactionException(exception);
            if (originalCause != null && isExceptionOfType(originalCause, CLASS_EXCEL_VALIDATION_EXCEPTION)) {
                return originalCause;
            }
        }
        
        // 递归检查异常链
        return findExceptionInCauseChain(exception, CLASS_EXCEL_VALIDATION_EXCEPTION);
    }
    
    /**
     * 提取业务异常
     * 
     * @param exception 原始异常
     * @return 提取出的BusinessException，如果不存在则返回null
     */
    public static Object extractBusinessException(Throwable exception) {
        if (exception == null) {
            return null;
        }
        
        // 如果异常本身就是BusinessException，直接返回
        if (isExceptionOfType(exception, CLASS_BUSINESS_EXCEPTION)) {
            return exception;
        }
        
        // 递归检查异常链
        return findExceptionInCauseChain(exception, CLASS_BUSINESS_EXCEPTION);
    }
    
    /**
     * 检查异常是否为指定类型
     */
    private static boolean isExceptionOfType(Throwable exception, String typeName) {
        return exception.getClass().getSimpleName().equals(typeName);
    }
    
    /**
     * 从事务异常中获取原始原因
     */
    private static Throwable getOriginalCauseFromTransactionException(Throwable exception) {
        try {
            java.lang.reflect.Method getOriginalCauseMethod = 
                exception.getClass().getMethod(METHOD_GET_ORIGINAL_CAUSE);
            return (Throwable) getOriginalCauseMethod.invoke(exception);
        } catch (Exception e) {
            log.warn("无法从TransactionProcessingException获取原始原因", e);
            return null;
        }
    }
    
    /**
     * 在异常链中查找指定类型的异常
     */
    private static Object findExceptionInCauseChain(Throwable exception, String exceptionType) {
        Throwable cause = exception.getCause();
        while (cause != null) {
            if (isExceptionOfType(cause, exceptionType)) {
                return cause;
            }
            cause = cause.getCause();
        }
        return null;
    }
    
    /**
     * 处理Sheet异常，提取并分类异常，记录日志和决定如何处理异常
     * 该方法旨在简化ExcelTransactionServiceImpl中的异常处理逻辑
     * 
     * @param e 原始异常
     * @param sheetNo Sheet编号
     * @param sheetName Sheet名称
     * @param phase 处理阶段描述（例如"校验"或"处理"）
     * @return 异常处理结果
     */
    public static ExceptionHandlingResult handleSheetException(Exception e, int sheetNo, 
                                                          String sheetName, String phase) {
        if (e == null) {
            String errorMsg = String.format("Sheet[%d:%s] %s阶段出现未知异常", sheetNo, sheetName, phase);
            return createOtherExceptionResult(errorMsg, new ExcelProcessingException(
                errorMsg, -1, -1, sheetName, sheetNo));
        }

        // 提取业务异常
        Object businessExObj = extractBusinessException(e);
        if (businessExObj != null) {
            return handleBusinessException(businessExObj, sheetNo, sheetName, phase);
        }
        
        // 提取验证异常
        Object validationExObj = extractValidationException(e);
        if (validationExObj != null) {
            return handleValidationException(validationExObj, sheetNo, sheetName, phase);
        }
        
        // 处理其他异常
        return handleGenericException(e, sheetNo, sheetName, phase);
    }
    
    /**
     * 处理业务异常
     */
    private static ExceptionHandlingResult handleBusinessException(Object businessExObj, 
                                                               int sheetNo, 
                                                               String sheetName, 
                                                               String phase) {
        RuntimeException businessEx = (RuntimeException) businessExObj;
        String errorMsg = businessEx.getMessage();
        log.error("Sheet[{}:{}]{}失败(业务异常): {}", sheetNo, sheetName, phase, errorMsg, businessEx);
        
        return new ExceptionHandlingResult(
            ExceptionType.BUSINESS,
            businessEx,
            errorMsg,
            true
        );
    }
    
    /**
     * 处理验证异常
     */
    private static ExceptionHandlingResult handleValidationException(Object validationExObj, 
                                                                int sheetNo, 
                                                                String sheetName, 
                                                                String phase) {
        RuntimeException validationEx = (RuntimeException) validationExObj;
        String errorMsg = validationEx.getMessage();
        log.error("Sheet[{}:{}]{}失败(验证异常): {}", sheetNo, sheetName, phase, errorMsg, validationEx);
        
        return new ExceptionHandlingResult(
            ExceptionType.VALIDATION,
            validationEx,
            errorMsg,
            true
        );
    }
    
    /**
     * 处理一般异常
     */
    private static ExceptionHandlingResult handleGenericException(Exception e, 
                                                             int sheetNo, 
                                                             String sheetName, 
                                                             String phase) {
        String errorMsg = String.format("Sheet[%d:%s] %s失败: %s", sheetNo, sheetName, phase, e.getMessage());
        log.error(errorMsg, e);
        
        return createOtherExceptionResult(errorMsg, e);
    }
    
    /**
     * 创建其他类型异常的处理结果
     */
    private static ExceptionHandlingResult createOtherExceptionResult(String errorMsg, Throwable cause) {
        return new ExceptionHandlingResult(
            ExceptionType.OTHER,
            new RuntimeException(errorMsg, cause),
            errorMsg,
            true
        );
    }
    
    /**
     * 处理Excel处理异常，提取并分类异常，记录日志和决定如何处理异常
     * 
     * @param e 原始异常
     * @param phase 处理阶段描述（例如"数据校验"或"数据保存"）
     * @return 异常处理结果
     */
    public static ExceptionHandlingResult handleExcelProcessingException(Exception e, String phase) {
        if (e == null) {
            String errorMsg = String.format("%s阶段出现未知异常", phase);
            return createOtherExceptionResult(errorMsg, new ExcelProcessingException(
                errorMsg, -1, -1, "未知", -1));
        }

        // 提取业务异常
        Object businessExObj = extractBusinessException(e);
        if (businessExObj != null) {
            RuntimeException businessEx = (RuntimeException) businessExObj;
            String errorMsg = businessEx.getMessage();
            log.error("{}失败(业务异常): {}", phase, errorMsg, businessEx);
            
            return new ExceptionHandlingResult(
                ExceptionType.BUSINESS,
                businessEx,
                errorMsg,
                true
            );
        }
        
        // 提取验证异常
        Object validationExObj = extractValidationException(e);
        if (validationExObj != null) {
            RuntimeException validationEx = (RuntimeException) validationExObj;
            String errorMsg = validationEx.getMessage();
            log.error("{}失败(验证异常): {}", phase, errorMsg, validationEx);
            
            return new ExceptionHandlingResult(
                ExceptionType.VALIDATION,
                validationEx,
                errorMsg,
                true
            );
        }
        
        // 处理其他异常
        String errorMsg = String.format("%s失败: %s", phase, e.getMessage());
        log.error(errorMsg, e);
        
        return createOtherExceptionResult(errorMsg, e);
    }
    
    /**
     * 记录验证异常的详细信息
     * 
     * @param validationEx 验证异常对象
     * @param source 异常来源
     * @param context 额外的上下文信息
     */
    public static void logValidationException(Object validationEx, String source, String context) {
        if (validationEx == null) {
            log.warn("尝试记录空的验证异常，来源: {}, 上下文: {}", source, context);
            return;
        }

        try {
            String exceptionClassName = validationEx.getClass().getSimpleName();
            
            if (CLASS_EXCEL_VALIDATION_EXCEPTION.equals(exceptionClassName)) {
                logDetailedValidationInfo(validationEx, source, context);
            } else {
                logBasicValidationInfo(validationEx, source, context);
            }
        } catch (Exception e) {
            // 反射失败时，回退到基本日志
            if (validationEx instanceof Throwable throwable) {
                log.error("[{}] 验证异常(无法获取详情): {}, 上下文: {}", 
                        source, 
                        throwable.getMessage(), 
                        context,
                        throwable);
            } else {
                log.error("[{}] 验证异常(无法获取详情): {}, 上下文: {}", 
                        source, 
                        validationEx, 
                        context);
            }
        }
    }
    
    /**
     * 记录详细的验证信息（带反射）
     */
    private static void logDetailedValidationInfo(Object validationEx, String source, String context) 
            throws ReflectiveOperationException {
        int rowIndex = (int) validationEx.getClass().getMethod(METHOD_GET_ROW_INDEX).invoke(validationEx);
        Object errorMessages = validationEx.getClass().getMethod(METHOD_GET_ERROR_MESSAGES).invoke(validationEx);
        String sheetName = (String) validationEx.getClass().getMethod(METHOD_GET_SHEET_NAME).invoke(validationEx);
        int sheetIndex = (int) validationEx.getClass().getMethod(METHOD_GET_SHEET_INDEX).invoke(validationEx);
        
        // 格式化并记录错误信息
        if (validationEx instanceof Throwable throwable) {
            log.error("[{}] 验证失败: Sheet[{}:{}] 第{}行 - {}, 上下文: {}", 
                    source,
                    sheetIndex,
                    sheetName,
                    rowIndex,
                    errorMessages.toString(),
                    context,
                    throwable);
        } else {
            log.error("[{}] 验证失败: Sheet[{}:{}] 第{}行 - {}, 上下文: {}", 
                    source,
                    sheetIndex,
                    sheetName,
                    rowIndex,
                    errorMessages.toString(),
                    context);
        }
    }
    
    /**
     * 记录基本的验证信息（无反射）
     */
    private static void logBasicValidationInfo(Object validationEx, String source, String context) {
        if (validationEx instanceof Throwable throwable) {
            log.error("[{}] 验证异常: {}, 上下文: {}", 
                    source, 
                    throwable.getMessage(),
                    context,
                    throwable);
        } else {
            log.error("[{}] 验证异常: {}, 上下文: {}", 
                    source, 
                    validationEx,
                    context);
        }
    }
    
    /**
     * 记录验证异常的详细信息（向后兼容方法）
     * 
     * @param validationEx 验证异常对象
     * @param source 异常来源
     */
    public static void logValidationException(Object validationEx, String source) {
        logValidationException(validationEx, source, "无附加上下文");
    }
    
    /**
     * 获取异常的根本原因
     * 
     * @param exception 原始异常
     * @return 根本原因异常
     */
    public static Throwable getRootCause(Throwable exception) {
        if (exception == null) {
            return null;
        }
        
        Throwable cause = exception.getCause();
        if (cause == null) {
            return exception;
        }
        
        Throwable rootCause = exception;
        while (cause != null && cause != rootCause) {
            rootCause = cause;
            cause = cause.getCause();
        }
        
        return rootCause;
    }

    /**
     * 处理Sheet处理过程中的异常 - 提供向后兼容的API
     * 
     * @param e 异常
     * @param sheetNo Sheet编号
     * @param sheetName Sheet名称
     * @param failedSheets 失败Sheet的记录Map
     * @param originalExceptions 原始异常的记录Map
     */
    public static void handleSheetException(Exception e, int sheetNo, String sheetName, 
                                         Map<Integer, String> failedSheets, 
                                         Map<Integer, Throwable> originalExceptions) {
        // 使用新的方法处理异常
        ExceptionHandlingResult result = 
            handleSheetException(e, sheetNo, sheetName, "处理");
        
        // 记录结果到提供的映射中
        failedSheets.put(sheetNo, result.getErrorMessage());
        originalExceptions.put(sheetNo, result.getException());
    }
    
    /**
     * 处理导入过程中的异常
     *
     * @param e 异常
     * @throws RuntimeException 总是抛出处理后的异常
     */
    public static void handleImportException(Exception e) {
        RuntimeException processedException;
        
        if (e == null) {
            processedException = new ExcelProcessingException("导入过程中发生未知异常", -1, -1, "未知", -1);
        } else {
            // 提取业务异常
            Object businessExObj = extractBusinessException(e);
            if (businessExObj instanceof RuntimeException businessEx) {
                processedException = businessEx;
            }
            // 提取验证异常
            else {
                Object validationExObj = extractValidationException(e);
                if (validationExObj instanceof RuntimeException validationEx) {
                    processedException = validationEx;
                } else {
                    // 获取根本原因
                    Throwable rootCause = getRootCause(e);
                    String errorMessage = rootCause != null ? rootCause.getMessage() : e.getMessage();
                    
                    // 创建一个具体的导入异常
                    processedException = new ExcelProcessingException(
                        "导入失败: " + errorMessage, 
                        e,
                        -1, 
                        -1, 
                        "未知", 
                        -1
                    );
                }
            }
        }
        
        // 总是抛出异常，确保返回值被使用
        throw processedException;
    }
}